﻿using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Linq;
using MagicSir.Abstractions;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace MagicSir.DependencyInjection
{
    public static class DependencyInjectionExtensions
    {
        internal static IServiceCollection AddDepencyInjection(this IServiceCollection services)
        {
            Inject(services);
            return services;
        }

        static IEnumerable<Assembly> GetAssemblies()
        {
            var assembilies =
                from assembly in AppDomain.CurrentDomain.GetAssemblies()
                where assembly.ExportedTypes.Any(t =>
                   t.IsInterface == false
                   && t.IsAbstract == false
                   &&

                (typeof(IScoped).IsAssignableFrom(t)
                  || typeof(ISingleton).IsAssignableFrom(t)
                  || typeof(ITransient).IsAssignableFrom(t))

                )
                select assembly;
            ;
            return assembilies;
        }
        static void Inject(IServiceCollection services)
        {
            var assemblies = GetAssemblies();
            var types =
                from assembly in assemblies
                from t in assembly.ExportedTypes
                where t.IsInterface == false
                         && t.IsAbstract == false
                         && (typeof(IScoped).IsAssignableFrom(t)
                        || typeof(ISingleton).IsAssignableFrom(t)
                        || typeof(ITransient).IsAssignableFrom(t))
                select t;
            types = types.Distinct();
            foreach (var type in types.Where(typeof(ISingleton).IsAssignableFrom))
            {
                InjectSingleton(services, type);
            }
            foreach (var type in types.Where(typeof(IScoped).IsAssignableFrom))
            {
                InjectScoped(services, type);
            }
            foreach (var type in types.Where(typeof(ITransient).IsAssignableFrom))
            {
                InjectTransient(services, type);
            }
        }

        static void InjectSingleton(IServiceCollection services, Type type)
        {
            var interfaces = type.GetInterfaces();
            if (interfaces.Any(i => i != typeof(ISingleton)))
            {
                foreach (var i in interfaces.Where(i => i != typeof(ISingleton)))
                {
                    services.TryAddSingleton(i, type);
                }
            }
            services.TryAddSingleton(type);

        }

        static void InjectScoped(IServiceCollection services, Type type)
        {
            var interfaces = type.GetInterfaces();
            if (interfaces.Any(i => i != typeof(IScoped)))
            {
                foreach (var i in interfaces.Where(i => i != typeof(IScoped)))
                {
                    services.TryAddScoped(i, type);
                }
            }
            services.TryAddScoped(type);
        }

        static void InjectTransient(IServiceCollection services, Type type)
        {
            var interfaces = type.GetInterfaces();
            if (interfaces.Any(i => i != typeof(ITransient)))
            {
                foreach (var i in interfaces.Where(i => i != typeof(ITransient)))
                {
                    services.TryAddTransient(i, type);
                }
            }
            services.TryAddTransient(type);
        }
    }
}
